package com.yjzx.util.service.pay.strategy.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayOrderQueryRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.yjzx.util.service.pay.config.properties.WechatProperties;
import com.yjzx.util.service.pay.constants.BaseConstant;
import com.yjzx.util.service.pay.enums.ClientEnum;
import com.yjzx.util.common.util.TimeUtils;
import com.yjzx.util.service.pay.strategy.MiniOrderInterface;
import com.yjzx.util.service.pay.strategy.OrderInterface;
import com.yjzx.util.service.pay.strategy.PayStrategy;
import com.yjzx.util.service.pay.strategy.PaySuccessParam;
import com.yjzx.util.common.util.IpUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.awt.image.BufferedImage;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * @author yjzx
 * @Description 微信支付
 * @create 2022-12-28 22:40
 */
@Slf4j
@Component
public class WeixinPayStrategy implements PayStrategy {

    @Getter
    final private String name = "weixin";
    private final WechatProperties wechatProperties;
    private final WxMaService wxMaService;
    Map<String, WxPayService> wxPayService;

    public WeixinPayStrategy( WechatProperties wechatProperties,WxMaService wxMaService, Map<String, WxPayService> wxPayService) {
        this.wechatProperties = wechatProperties;
        this.wxPayService = wxPayService;
        this.wxMaService = wxMaService;
    }

    private WxPayService getWxPayService(ClientEnum clientEnum) {
        WxPayService wxPayService;
        switch (clientEnum) {
            case APP:
                wxPayService = this.wxPayService.get("wxPayAppService");
                break;
            case MINI:
                wxPayService = this.wxPayService.get("wxPayMiniService");
                break;
            case HTML:
            default:
                wxPayService = this.wxPayService.get("wxPayHtmlService");

        }
        return wxPayService == null ? getWxPayService(ClientEnum.getDefaultClient()) : wxPayService;
    }

    private WechatProperties.WxApplication getApplication(ClientEnum clientEnum) {
        WechatProperties.WxApplication application;
        switch (clientEnum) {
            case APP:
                application = this.wechatProperties.getApp();
                break;
            case MINI:
                application = this.wechatProperties.getMini();
                break;
            case HTML:
            default:
                application = this.wechatProperties.getHtml();
        }
        return application == null ? getApplication(ClientEnum.getDefaultClient()) : application;
    }

    /**
     * 为某个订单展示收银台页面
     *
     * @param orderInfo 订单信息
     * @return
     */
    @Override
    public String cashierPage(OrderInterface orderInfo) {
        ClientEnum clientEnum = orderInfo.getClientEnum();
        WxPayService wxPayService = getWxPayService(clientEnum);
        WechatProperties.WxApplication application = getApplication(clientEnum);

        //调用统一下单API
        WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
        request.setBody(orderInfo.getDesc());
        request.setOutTradeNo(orderInfo.getOutTradeNo());
        // 单位为分
        request.setTotalFee(orderInfo.getPrice().multiply(new BigDecimal("100")).intValue());
        HttpServletRequest servletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        request.setSpbillCreateIp(IpUtil.getRemoteHost(servletRequest));
        request.setNotifyUrl(application.getNotifyUrl());
        // 交易类型，此处以生成二维码支付为例
        ;
        try {
            switch (clientEnum) {

                case MINI:
                    request.setTradeType(BaseConstant.JS_API);
                    String openId = ((MiniOrderInterface) orderInfo).getOpenId();
                    request.setOpenid(openId);
                    // 请求body参数
                    return wxPayService.createOrder(request);
                case APP:
                    request.setTradeType(BaseConstant.APP);
                    return wxPayService.createOrder(request);
                case HTML:
                default:
                    request.setTradeType(BaseConstant.NATIVE);
                    // 请求body参数
                    WxPayNativeOrderResult result = wxPayService.createOrder(request);
                    return createQRCode(result.getCodeUrl(), 400, 400);

            }
        } catch (WxPayException e) {
            e.printStackTrace();
        }


        return null;
    }

    /**
     * 验证签名
     *
     * @param request    原生请求
     * @param body       请求体数据
     * @param clientEnum
     * @return
     */
    @Override
    public boolean checkSign(HttpServletRequest request, String body, ClientEnum clientEnum) {
        try {
            WxPayService wxPayService = getWxPayService(clientEnum);

            final WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(body);
            if (BaseConstant.SUCCESS.equals(notifyResult.getResultCode())) {
                return true;
            }

            return false;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 验签错误处理
     *
     * @return
     */
    @Override
    public Object signError() {
        //微信验签失败 返回 500状态码就行。  我们用异常机制快速构造错误返回
        throw new RuntimeException("签名验证失败");
    }

    /**
     * 验签成功后处理通知数据： 把通知的所有数据封装指定对象
     *
     * @param request 原生请求
     * @param body    请求体数据
     * @return
     */
    @Override
    public PaySuccessParam process(HttpServletRequest request, String body) {
        //微信验签成功  返回正确数据；
        Map<String, String> map = JSON.parseObject(body, new TypeReference<Map<String, String>>() {
        });
        PaySuccessParam successParam = new PaySuccessParam();
        successParam.setOrderNum(map.get("out_trade_no"));
        successParam.setOutNum(map.get("trade_no"));
        successParam.setPayTime(TimeUtils.stringToDateTime(map.get("gmt_payment")));
        successParam.setPayAccount(map.get("buyer_logon_id"));
        return successParam;
    }

    @Override
    public boolean payChargebacks(String orderId, ClientEnum clientEnum) {

        return false;
    }

    @Override
    public PaySuccessParam payQuery(String orderId, ClientEnum clientEnum) {
        WxPayService wxPayService = getWxPayService(clientEnum);
        WxPayOrderQueryRequest wxPayOrderQueryRequest = new WxPayOrderQueryRequest();
        wxPayOrderQueryRequest.setOutTradeNo(orderId);
        try {
            WxPayOrderQueryResult result = wxPayService.queryOrder(wxPayOrderQueryRequest);
            if (BaseConstant.SUCCESS.equals(result.getResultCode()) && BaseConstant.SUCCESS.equals(result.getTradeState())) {
                PaySuccessParam successParam = new PaySuccessParam();
                successParam.setOrderNum(result.getOutTradeNo());
                successParam.setOutNum(result.getTransactionId());
                successParam.setPayTime(TimeUtils.stringToDateTime(result.getTimeEnd()));
                successParam.setPayAccount(result.getOpenid());
                return successParam;
            }
        } catch (WxPayException e) {
            throw new RuntimeException("查询微信支付状态失败!");
        }
        return null;
    }

    @Override
    public String getMiniOpenId(String id) {
        try {
            WxMaJscode2SessionResult info = wxMaService.getUserService().getSessionInfo(id);
            if (info != null) {
                return info.getOpenid();
            }
        } catch (WxErrorException e) {
        }
        return null;
    }

    /**
     * 验签通过返回
     *
     * @return
     */
    @Override
    public Object signOk() {
        Map<String, Object> resp = new HashMap<>();
        resp.put("code", "SUCCESS");
        resp.put("message", "成功");
        return resp;
    }

    /**
     * 支持哪种支付
     *
     * @param type 支付類型
     * @return
     */
    @Override
    public boolean supports(String type) {
        return getName().equalsIgnoreCase(type);
    }

    /**
     * 生成二维码
     *
     * @param content
     * @param width
     * @param height
     * @return
     */
    private String createQRCode(String content, int width, int height) {
        //1、生成二维码
        BufferedImage image = QrCodeUtil.generate(content, width, height);

        String png = "data:image/png;base64," + ImgUtil.toBase64(image, "png");

        return png;
    }
}
